package cn.marknum.gateway.filter;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.marknum.feign.clients.TokenFeginClient;
import cn.marknum.pojo.sys.JcmkYh;
import cn.marknum.redis.constants.RedisKey;
import cn.marknum.redis.service.TokenYhService;
import cn.marknum.web.ErrCode;
import cn.marknum.web.response.Response;
import cn.marknum.web.response.ResponseDSL;
import com.alibaba.fastjson2.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @Description: 全局过滤器
 * @Author: wangxiaohong
 * @Date: 15:08 2019-08-22
 */
@Component
public class TokenFilter implements GlobalFilter, Ordered {

    private TokenYhService tokenYhService;

    @Resource
    private TokenFeginClient tokenFeginClient;

    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private AuthUtil authUtil;

    private final static long TTLTHRESHOLD = 2400;

    private static final Logger LOGGER = LoggerFactory.getLogger(TokenFilter.class);
    private static final Map<Integer, Long> map = new HashMap<>();
    //刷新token时间
    private static Integer refreshTime = 5 * 60;

    @Autowired
    public void setTokenYhService(TokenYhService tokenYhService) {
        this.tokenYhService = tokenYhService;
    }

    @Autowired
    public void setStringRedisTemplate(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        LOGGER.debug("准备鉴权");
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        HttpHeaders header = request.getHeaders();
        String token = header.getFirst(HttpHeaders.AUTHORIZATION);

        String uri = request.getURI().toString();
        String path = request.getPath().toString();
        boolean flag = false;
        if (token == null || token.isEmpty()) {
            LOGGER.debug("未获取到令牌：{}", path);
            //放行不需要token的页面
            return filterPermission(path, exchange, chain);
        }
        LOGGER.debug("准备查询用户信息");
        //读取用户信息
        JcmkYh jcmkYh = tokenYhService.findById(token);
        //上次刷新时间
        if (jcmkYh != null) {
            LOGGER.debug("获取到用户信息");
            Long time = map.get(jcmkYh.getId());
            if (time != null) {
                Long currenTime = System.currentTimeMillis() / 1000;
                if ((currenTime - time) > refreshTime) {
                    tokenFeginClient.refreshToken(token, jcmkYh.getTokenKey());
                    map.put(jcmkYh.getId(), System.currentTimeMillis() / 1000);
                }
            } else {
                map.put(jcmkYh.getId(), System.currentTimeMillis() / 1000);
            }
        }
        //如果token为空
        if (jcmkYh == null) {
            LOGGER.debug("未能获取到用户信息");
            //放行不需要token的页面
            return filterPermission(path, exchange, chain);
        } else {
            LOGGER.debug("开始判断用户资源权限");
            //判断资源权限
            flag = filterRole(jcmkYh, exchange, chain, path);
        }
        if (!flag) {
            LOGGER.debug("path:[{}]无权限，提示用户无权限", uri);
            return getVoidMono(exchange.getResponse(), ErrCode.PERMISSION_DEAD);
        }
        LOGGER.debug("放行");
        return chain.filter(exchange);
    }

    private boolean filterRole(JcmkYh jcmkYh, ServerWebExchange exchange, GatewayFilterChain chain, String qqUrl) {
        //   JcmkYh jcmkYh = (JcmkYh) redisService.get(RedisKey.USER_TOKEN + token);
        //过滤不需要判断的url
        if (authUtil.filtration(qqUrl)) {
            return true;
        }
        //如果不是超级管理员
        if (jcmkYh.getCjgly() != 1) {
            // 角色验证
            // 获取该角色下的所有请求
            Set<String> zyList = stringRedisTemplate.opsForSet().members(RedisKey.JCMK_ZY_AUTH_PATH + jcmkYh.getSsJs());
            //查看是否有权限
            return isResource(zyList, qqUrl);
        } else {
            return true;
        }
    }

    //查看是否有权限
    private boolean isResource(Set<String> zyList, String qqUrl) {
        //用户没有任务资源，则无任何权限
        if (CollUtil.isEmpty(zyList)) {
            return false;
        }
        //去掉参数
        if (qqUrl.contains("?")) {
            qqUrl = qqUrl.substring(0, qqUrl.indexOf("?"));
        }
        for (String url : zyList) {
            if (StrUtil.isBlank(url)) {
                continue;
            }
            String tmp;
            if (url.contains("#")) {
                tmp = ".*" + url.replaceAll("#+", "\\\\d+") + "$";
            } else if (url.contains("**")) {
                tmp = ".*" + url.replaceAll("\\*\\*", ".*") + "$";
            } else if (url.contains("*")) {
                tmp = ".*" + url.replaceAll("\\*", "[0-9a-zA-Z]+") + "$";
            } else {
                tmp = ".*" + url + "$";
            }
            if (qqUrl.matches(tmp)) {
                return true;
            }
        }
        return false;
    }


    private Mono<Void> filterPermission(String uri, ServerWebExchange exchange, GatewayFilterChain chain) {
        if (!authUtil.filtration(uri)) {
            LOGGER.debug("无权限，返回未登录提示");
            return getVoidMono(exchange.getResponse(), ErrCode.LOGIN_EXPIRE);
        } else {
            LOGGER.debug("无需鉴权");
            return chain.filter(exchange);
        }
    }

    @Override
    public int getOrder() {
        return -4;
    }

    private Mono<Void> getVoidMono(ServerHttpResponse serverHttpResponse, ErrCode errCode) {
        serverHttpResponse.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        Response responseResult = ResponseDSL.failure(errCode.getTipCode(), errCode.getTipMsg());
        DataBuffer dataBuffer =
                serverHttpResponse.bufferFactory().wrap(JSONObject.toJSONString(responseResult).getBytes(StandardCharsets.UTF_8));
        return serverHttpResponse.writeWith(Flux.just(dataBuffer));
    }

}
